﻿using System;
using System.Collections.Generic;
using Acando.Dynamics.CRM.Silverlight.Library.Helper;
using Acando.Dynamics.CRM.Silverlight.Library.MetaSdk;
using Acando.Dynamics.CRM.Silverlight.Library.Model;
using Acando.Dynamics.CRM.Silverlight.Library.CrmSdk;
using RiaCrm.Infrastructure;
using Acando.Dynamics.CRM.Silverlight.Library.Interfaces;


namespace RiaCrm.DataAccessLayer
{
    public class MetaDataLayer : IMetaDataLayer
    {
        private static Dictionary<string, EntityMetadata> cache = new Dictionary<string, EntityMetadata>();
        private String _entitylogicalname;
        private String _organame;

        public delegate void MetadataLoadedHandler(object sender, EntityMetadata metadata);
        public delegate void LayountXmlLoadedHandler(object sender, String result);

        public event MetadataLoadedHandler LoadMetaDataCompleted;
        public event LayountXmlLoadedHandler LoadLayountXmlCompleted;


        // organame is a registered instance in the container, see Bootstrapper
        public MetaDataLayer(InitParaOrgaName organame)
        {
            this._organame = organame.Value;
        }

        public void LoadMetaData(String entitylogicalname, Action<EntityMetadata> loadCompletedCallback)
        {
            this._entitylogicalname = entitylogicalname;

            lock (typeof(MetaDataLayer))
            {
                if (!cache.ContainsKey(entitylogicalname))
                {
                    
                    if (!IsolatedStoreHelper.GetSiteSettings().Contains(entitylogicalname))
                    {
                        MetadataServiceSoapClient ms = SoapClientFactory.CreateMetaIntanceSilver();
                        this.LoadMetaDataCompleted += (sender, args) => loadCompletedCallback(args); 
                      
                        ms.ExecuteCompleted += new EventHandler<Acando.Dynamics.CRM.Silverlight.Library.MetaSdk.ExecuteCompletedEventArgs>(metaService_ExecuteCompleted);
                        
                        RetrieveEntityRequest request = new RetrieveEntityRequest();
                        request.RetrieveAsIfPublished = true;
                        request.LogicalName = entitylogicalname;
                        request.EntityItems = EntityItems.IncludeAttributes | EntityItems.IncludeRelationships;

                        WrapperServiceCall.ExecuteWrapper(ms, request, this._organame);
                    }
                    else
                    {
                        EntityMetadata metaDataObj = IsolatedStoreHelper.GetValueFromApplicationSettings<EntityMetadata>(entitylogicalname);
                       
                        cache[entitylogicalname] = metaDataObj;
                        loadCompletedCallback(metaDataObj);
                    }
                }
                else
                    loadCompletedCallback(cache[entitylogicalname]);
            }
        }

        private void metaService_ExecuteCompleted(object sender, Acando.Dynamics.CRM.Silverlight.Library.MetaSdk.ExecuteCompletedEventArgs e)
        {
            RetrieveEntityResponse entityResponse = e.Result as RetrieveEntityResponse;

            if (e.Error == null)
            {
                // Add Data to Cache:
                cache[entityResponse.EntityMetadata.LogicalName] = entityResponse.EntityMetadata;

                IsolatedStoreHelper.AddvalueToApplicationSettings<EntityMetadata>(entityResponse.EntityMetadata.LogicalName,
                                                                                  entityResponse.EntityMetadata);

                // Trigger event:
                LoadMetaDataCompleted(this, entityResponse.EntityMetadata);
                
            }
            else
                throw e.Error;
        }

        public void LoadLayountXmlData(EntityMetadata entitymetadata, Action<String> loadLayoutxmlCompletedCallback)
        {
            this._entitylogicalname = entitymetadata.LogicalName;

            // check layoutxml already exist in IsolationStore
            if (!IsolatedStoreHelper.FileExists("savedquery", entitymetadata.LogicalName))
            {
                GenericDataModel datamodel = new GenericDataModel(typeof(savedquery), this._organame);
                datamodel.ExecuteCompleted += new EventHandler<Acando.Dynamics.CRM.Silverlight.Library.CrmSdk.ExecuteCompletedEventArgs>(datamodel_ExecuteCompleted);

                this.LoadLayountXmlCompleted += (sender, args) => loadLayoutxmlCompletedCallback(args);

                ColumnSet cols = new ColumnSet();
                cols.Attributes = new String[] { "layoutxml" };

                datamodel.Request(new QueryByAttribute()
                {
                    ColumnSet = cols, 
                    Attributes = new string[] { "isdefault", "returnedtypecode", "querytype" },
                    Values = new object[] { true, entitymetadata.ObjectTypeCode.Value, 0 }
                });
            }
            else
            {
                String layoutXmlFromStore = IsolatedStoreHelper.GetDataFromStore("savedquery", entitymetadata.LogicalName);
                loadLayoutxmlCompletedCallback(layoutXmlFromStore);
            }
        }

        private void datamodel_ExecuteCompleted(object sender, Acando.Dynamics.CRM.Silverlight.Library.CrmSdk.ExecuteCompletedEventArgs e)
        {
            var response = e.Result as RetrieveMultipleResponse;

            DynamicEntity dyn = response.BusinessEntityCollection.BusinessEntities[0] as DynamicEntity;

            
            IsolatedStoreHelper.StoreData( "savedquery",
                                           this._entitylogicalname,
                                           dyn["layoutxml"].ToString());

            this.LoadLayountXmlCompleted(this, dyn["layoutxml"].ToString());
        }
    
        public void LoadMetaDataOfReferencedEntity(String attributeName, EntityMetadata metaData,  Action<EntityMetadata> loadCompletedCallback)
        {
            String errorMsg = String.Format("The attribute '{0}' belongs not to a referenced entity.", attributeName);
            Boolean foundAttribute = false;


            if (attributeName.Contains(".")) // sample: 'accountprimarycontactidcontactcontactid.emailaddress1'
            {
                String head = attributeName.Split(new char[] { '.' })[0];   // e.g.: 'accountprimarycontactidcontactcontactid'
                String tail = attributeName.Split(new char[] { '.' })[1];   // e.g.: 'emailaddress1'

                // In case the view uses a field of a refereneced Entity, the metaData needs to be loaded in addition
                foreach (var relationship in metaData.ManyToOneRelationships)
                {
                    
                    String fullname = String.Format("{0}{1}{2}{3}", relationship.ReferencingEntity,
                                                                    relationship.ReferencingAttribute,
                                                                    relationship.ReferencedEntity, 
                                                                    relationship.ReferencedAttribute);

                    if (fullname.ToLower() == head.ToLower())
                    {
                        foundAttribute = true;
                        this.LoadMetaData(relationship.ReferencedEntity, loadCompletedCallback);
                    }
                }

                if (!foundAttribute)
                    throw new Exception(errorMsg);
            }
            else
                throw new Exception(errorMsg); 
        }
    }
}
